package session

import (
	"dmsv/agent"
	"dmsv/call"
	"dmsv/callrouter"
	"dmsv/common"
	"errors"
	"fmt"
	"github.com/beego/beego/v2/core/logs"
	"time"
)

type LegDir string

const (
	LEG_DIR         LegDir = ""
	LEG_DIR_USER    LegDir = "user"
	LEG_DIR_SERVICE LegDir = "service"
	LEG_DIR_THIRD   LegDir = "outline"
)

type HangStatus string

const (
	HANG_UP_TIMEOUT  HangStatus = "call_timeout"
	HANG_UP_NOANSWER HangStatus = "not_answer"
	HANG_UP_USER     HangStatus = "close_by_user"
	HANG_UP_API      HangStatus = "close_by_api"
	HANG_UP_SYSTEM   HangStatus = "close_by_system"
	HANG_UP_ERR      HangStatus = "close_by_err"
	HANG_UP_SERVICE  HangStatus = "close_by_service"
	HANG_UP_LINEERR  HangStatus = "line_err"
	HANG_UP_REFUSE   HangStatus = "user_refuse"
	HANG_UP_POWEROFF HangStatus = "user_poweroff"
)

type CallEvent int

const (
	CALL_EVENT_FAIL CallEvent = iota
	CALL_EVENT_START
	CALL_EVENT_RING
	CALL_EVENT_DTMF
	CALL_EVENT_TO_SERVICE
	CALL_EVENT_TO_NUMBER
	CALL_EVENT_PLAY_OVER
	CALL_EVENT_SATISFACTION
	CALL_EVENT_ANSWER
	CALL_EVENT_ENQUE
	CALL_EVENT_HANGUP
	CALL_EVENT_DESTROY
)

func ConvertCallStatusToString(e CallEvent) (resp string) {
	switch e {
	case CALL_EVENT_FAIL:
		resp = "call init fail"
	case CALL_EVENT_RING:
		resp = "call ringing"
	case CALL_EVENT_PLAY_OVER:
		resp = "call play over"
	case CALL_EVENT_ANSWER:
		resp = "call answer"
	case CALL_EVENT_HANGUP:
		resp = "call hangup"
	case CALL_EVENT_DTMF:
		resp = "dtmf event"
	case CALL_EVENT_ENQUE:
		resp = "enter queue"
	}
	return
}

type Bill struct {
	CallId  string     `json:"callId"`
	Start   string     `json:"start"`
	End     string     `json:"end"`
	Caller  string     `json:"caller"`
	Callee  string     `json:"callee"`
	Duation int        `json:"duation"`
	Dir     LegDir     `json:"dir"`
	Result  HangStatus `json:"result"`
}

type HangInfo struct {
	RingTime   time.Time
	AnswerTime time.Time
	HangUpTime time.Time
	Dir        HangStatus
	SipCode    string
	Record     string
	Result     string
	ServiceId  string
	Duration   int
	QueId      int
	Reason     interface{}
}

type DtmfEvent struct {
	Dtmf    string
	TimeOut bool
	KeyType string
}

func (this DtmfEvent) KeyIsEnque() bool {
	return this.KeyType == SS_DTMF_QUE
}

func (this DtmfEvent) KeyIsSatisfaction() bool {
	return this.KeyType == SS_DTMF_SATISFACTION
}

type CallBackData struct {
	Event     CallEvent
	CallStart time.Time
	CallId    string
	Caller    string
	Callee    string
	Dir       LegDir
	MsgTime   time.Time
	Ssion     *SessionInfo
	//Call      *CallInfo
	Data interface{}
}

func createCallback(appId, display, caller, callee, callId string, cb chan *CallBackData) (*SessionInfo, error) {

	ss := &SessionInfo{
		appId:     appId,
		display:   display,
		caller:    caller,
		callee:    callee,
		startTime: time.Now(),
		sessionId: callId,
		callType:  CALL_TYPE_BACK,
		call:      make(map[string]*call.CallInfo),
		evChan:    make(chan interface{}, 100),
		CbChan:    cb,
	}
	go ss.eventRoutine()
	gSessions.Store(callId, ss)

	logs.Debug("create new callback session:%v,caller:%v,callee:%v", callId, caller, callee)

	return ss, nil
}

func CallBackService(service *agent.AgentInfo, callId, callee, display string, cb chan *CallBackData) error {

	caller := service.GetServeNumber()

	ss, err := createCallback(service.GetAppId(), display, caller, callee, callId, cb)
	if err != nil {
		logs.Error("[%v]call service fail:%v", callId, err.Error())

		return errors.New("eInit")
	}

	addr, local := callrouter.GetRouterByNum(callee, caller)
	logs.Debug("get router %v,local:%v for service:%v,display:%v", addr, local, caller, callee)
	if local {

		sCall, err := call.OutCallService(callId, display, caller, ss.evChan)
		if err != nil {
			logs.Error("[%v]创建外呼失败...", callId)
			ss.Destroy(common.ErrSystem)
			return errors.New("eCall")
		}

		//保存坐席相关信息
		sCall.SetAgent(service)
		ss.uCallId = sCall.CallId()
		ss.call[ss.uCallId] = sCall

	} else {
		logs.Error("[%v] 回拨外线暂未实现", callId)
		return errors.New("eFail")
	}

	logs.Debug("[%v][%v]create seesion success.caller:%v,callee:%v,router:%v",
		callId, caller, callee, addr)

	return nil
}

func AddCallInNumber(numbers string, callback chan *CallBackData) error {

	if _, ok := gListenNumbers.Load(numbers); !ok {
		gListenNumbers.Store(numbers, callback)
		return nil
	} else {
		logs.Debug("add new listen number:%v failed,number is exist", numbers)
		return fmt.Errorf("number:%v is exist", numbers)
	}
}

func DelListenNumber(numbers string) {

	gListenNumbers.Delete(numbers)
}

func DesdroyTheCall(callId string) {

	if v, ok := gSessions.Load(callId); ok {
		ss := v.(*SessionInfo)
		ss.bDestroy = true
		//结束所有会话
		ss.HangUp(nil)
	} else {
		logs.Error("[%v]No session info", callId)
	}
}

func LoadSipAcct() error {

	return call.LoadSipAcct()
}

func DelRegAcct(acct, domain string) error {

	return call.DelRegAcct(acct, domain)
}

func GetSession(sessionId string) (*SessionInfo, bool) {
	ss, ok := gSessions.Load(sessionId)
	if ok {
		return ss.(*SessionInfo), true
	} else {
		return nil, false
	}
}

func HangUpTheCall(sessionId string) error {
	val, ok := gSessions.Load(sessionId)
	if ok {
		ss := val.(*SessionInfo)
		ss.HangUpByApi()

		return nil
	} else {
		logs.Error("挂机失败,sessionId[%v] 不存在", sessionId)

		return fmt.Errorf("NotExist")
	}
}
